﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FireStarter.Base;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using Firestarter.DB.Schema.Test.BO.TestUtils;
using Firestarter.DB_DomainModel.Logic.Test;
using FireStarterModeller.BO;
using FireStarterModeller.Test;
using FireStarterModeller.Test.BO;
using Habanero.Testability;
using Habanero.Util;
using NUnit.Framework;
using Rhino.Mocks;

namespace Firestarter.DB_DomainModel.Logic.Test.DBChangesImporterTests
{
    [TestFixture]
    //There are also corresponding test classes for the Change Types of Add, Update, Delete and None.
    public class TestDBChangesImporter : TestBase
    {
        //TODO Mark 30 Jul 2009: Implement the following Stories:
        //Story: Need to test the secenario where the database is empty
        //Story: Need to test that the Errors collection does not return null.

        [Test]
        public void Test_ImportDBChanges_WhenAddedDatabase_ShouldSetDatabaseToUnchanged()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase().ApproveForAdd();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition----------------
            database.AssertIsApprovedAndPendingChangeTypeIs(ChangeType.Add);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            database.AssertIsBackToApprovedAndPendingChangeTypeIsNone();
        }

        [Test]
        public void Test_ImportDBChagnes_WhenSolutionNull_ShouldRaiseArError()
        {
            //---------------Set up test pack-------------------
            DMSolution solution = null;
            IDBDatabase database = new DBDatabase();
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition----------------
            Assert.IsNull(solution);
            //---------------Execute Test ----------------------
            try
            {
                importer.ImportDBChanges(database, solution);
                Assert.Fail("expected ArgumentNullException");
            }
                //---------------Test Result -----------------------
            catch (ArgumentNullException ex)
            {
                StringAssert.Contains("Value cannot be null", ex.Message);
                StringAssert.Contains("solution", ex.ParamName);
            }
        }
        [Test]
        public void Test_ImportDBChagnes_WhenDatabaseNull_ShouldRaiseArError()
        {
            //---------------Set up test pack-------------------
            DMSolution solution = new DMSolution();
            IDBDatabase database = null;
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition----------------
            Assert.IsNull(database);
            //---------------Execute Test ----------------------
            try
            {
                importer.ImportDBChanges(database, solution);
                Assert.Fail("expected ArgumentNullException");
            }
                //---------------Test Result -----------------------
            catch (ArgumentNullException ex)
            {
                StringAssert.Contains("Value cannot be null", ex.Message);
                StringAssert.Contains("database", ex.ParamName);
            }
        }
        [Test]
        public void Test_ImportDBChanges_WhenSolutionNameEmpty_ShouldSetSolutionNameToDatabaseName()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase().ApproveForAdd();
            var expectedDBName = GetRandomString();
            database.DatabaseName = expectedDBName;
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            solution.SolutionName = "";
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition----------------
            Assert.IsNullOrEmpty(solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedDBName, solution.SolutionName);
        }
        [Test]
        public void Test_ImportDBChanges_WhenSolutionNameNull_ShouldSetSolutionNameToDatabaseName()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase().ApproveForAdd();
            var expectedDBName = GetRandomString();
            database.DatabaseName = expectedDBName;
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            solution.SolutionName = null;
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition----------------
            Assert.IsNullOrEmpty(solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedDBName, solution.SolutionName);
        }
        [Test]
        public void Test_ImportDBChanges_WhenSolutionNameNotNull_ShouldNOtSetSolutionNameToDatabaseName()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase().ApproveForAdd();
            database.DatabaseName = GetRandomString();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            var initialSolutonName = solution.SolutionName;
            //---------------Assert Precondition---------------- 
            Assert.AreEqual(initialSolutonName, solution.SolutionName);
            Assert.AreNotEqual(database.DatabaseName, solution.SolutionName);

            Assert.IsNotNullOrEmpty(solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(initialSolutonName, solution.SolutionName, "The solution should not have been renamed");
            Assert.AreNotEqual(database.DatabaseName, solution.SolutionName, "The solution should not have been renamed");
        }
        /*
       
                [Test]
                public void Test_ImportDBChanges_WhenSolutionNameNotNull_ShouldNotContainSpaces()
                {
                    //---------------Set up test pack-------------------
                    IDBDatabase database = new DBDatabase().ApproveForAdd();
                    database.DatabaseName = "Test database";
            
                    DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
                    solution.SolutionName = DMSolution.DEFAULT_NEW_SOLUTION_NAME;
                    DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            
                    //---------------Assert Precondition---------------- 
                    Assert.IsTrue(database.DatabaseName.Contains(" "));
                    Assert.IsNotNullOrEmpty(solution.SolutionName);
                    //---------------Execute Test ----------------------
                    importer.ImportDBChanges(database, solution);
                    //---------------Test Result -----------------------
                    Assert.IsFalse(solution.SolutionName.Contains(" "));


                }*/

        [Test]
        public void Test_ImportDBChanges_WhenSolutionNameNewSolution_ShouldPascalCaseDatabaseName()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase();
            const string expectedSolutionName = "TestDatabase";
            database.DatabaseName = "Test database";

            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            solution.SolutionName = DMSolution.DEFAULT_NEW_SOLUTION_NAME;
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition---------------- 
            Assert.AreNotEqual(expectedSolutionName, database.DatabaseName);
            Assert.AreNotEqual(expectedSolutionName, solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedSolutionName, solution.SolutionName);
        }       
        [Test]
        public void Test_ImportDBChanges_WhenDatabaseNameHasTrailingSpace_ShouldPascalCaseDatabaseName()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase();
            const string expectedSolutionName = "TestDatabase";
            database.DatabaseName = "   Test database   ";

            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            solution.SolutionName = DMSolution.DEFAULT_NEW_SOLUTION_NAME;
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition---------------- 
            Assert.AreNotEqual(expectedSolutionName, database.DatabaseName);
            Assert.AreNotEqual(expectedSolutionName, solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedSolutionName, solution.SolutionName);
        }  
     
        [Test]
        public void Test_ImportDBChanges_WhenDatabaseNameNull_ShouldNotRenameSolution()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase();
            const string expectedSolutionName = DMSolution.DEFAULT_NEW_SOLUTION_NAME;
            database.DatabaseName = null;

            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            solution.SolutionName = DMSolution.DEFAULT_NEW_SOLUTION_NAME;
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition---------------- 
            Assert.AreNotEqual(expectedSolutionName, database.DatabaseName);
            Assert.AreEqual(expectedSolutionName, solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedSolutionName, solution.SolutionName);
        }       
        [Test]
        public void Test_ImportDBChanges_WhenDatabaseNameHas__ShouldPascalCaseDatabaseName()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase();
            const string expectedSolutionName = "TestDatabase";
            database.DatabaseName = "   test_database   ";

            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            solution.SolutionName = DMSolution.DEFAULT_NEW_SOLUTION_NAME;
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            //---------------Assert Precondition---------------- 
            Assert.AreNotEqual(expectedSolutionName, database.DatabaseName);
            Assert.AreNotEqual(expectedSolutionName, solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedSolutionName, solution.SolutionName);
        }
        [Test]
        public void Test_ImportDBChanges_WhenSolutionNameIsNewSolution_ShouldSetSolutionNameToDatabaseName()
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase();
            database.DatabaseName = GetRandomString();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            solution.SolutionName = DMSolution.DEFAULT_NEW_SOLUTION_NAME;
            //---------------Assert Precondition---------------- 
            Assert.AreEqual(DMSolution.DEFAULT_NEW_SOLUTION_NAME, solution.SolutionName);
            Assert.AreNotEqual(database.DatabaseName, solution.SolutionName);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(database.DatabaseName, solution.SolutionName, "The solution should have been renamed");
        }

        private static string GetRandomString()
        {
            return RandomValueGen.GetRandomString();
        }

        [Test]
        public void Test_ImportDBChanges_WhenTablePendingChangesApprovedFalse_AndTableChangeTypeAdd_ShouldNotImportFKey()
        {
            //---------------Set up test pack-------------------
            var dbTable = TestUtilsDBTable.GetTestFactory()
                    .WithOne(table => table.ForeignKeys)
                    .WithValue(table => table.Approved, false)
                    .WithValue(table => table.PendingChangeType, ChangeType.Add)
                    .CreateValidBusinessObject();

            var dbForeignKey = dbTable.ForeignKeys[0];
            dbForeignKey.PendingChangeType = ChangeType.Add;
            var database = dbTable.Database.ApproveForAdd();

            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dbTable.ForeignKeys.Count);
            Assert.IsFalse(dbTable.PendingChangeApproved);
            Assert.AreEqual(ChangeType.Add,  dbTable.PendingChangeType);
            Assert.AreEqual(ChangeType.Add, dbForeignKey.PendingChangeType);
            Assert.IsTrue(dbForeignKey.PendingChangeApproved);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(ChangeType.Add, dbForeignKey.PendingChangeType, "Should not have processed the FK since the table is marked as PendingChagneApproved false");
            Assert.IsTrue(dbForeignKey.PendingChangeApproved);
        }

        /// <summary>
        /// FixBug_ #826: (If PendingChangeTypeApproved false for a table still tries to import the relationship)
        ///- If table is add and PendingChangeApproved = false then the FK will not be imported.
        ///- If the table.ChangeType == Delete then the FK will not be imported.
        ///- If the table. Change type is anything else then the it will import the Foreign Key as long as the foreign Key 
        ///   itself is marked as Approved.
        /// </summary>
        /// <param name="tableChangeApproved"></param>
        /// <param name="tableChangeType"></param>
        /// <param name="fkImported"></param>
        [TestCase(false, ChangeType.Add, false)]
        [TestCase(false, ChangeType.Update, true)]
        [TestCase(false, ChangeType.Delete, false)]
        public void Test_ImportDBChanges_ImportFK_FixBug_826(bool tableChangeApproved, ChangeType tableChangeType, bool fkImported)
        {
            //---------------Set up test pack-------------------
            IDBDatabase database = new DBDatabase().ApproveForAdd();

            var dbTable = TestUtilsDBTable.GetTestFactory()
                    .WithValue(table => table.Database, database)
                    .WithOne(table => table.ForeignKeys)
                    .WithValue(table => table.Approved, tableChangeApproved)
                    .WithValue(table => table.PendingChangeType, tableChangeType)
                    .CreateValidBusinessObject();

            var dbForeignKey = dbTable.ForeignKeys[0];
            const ChangeType initialFKChangeType = ChangeType.Add;
            dbForeignKey.PendingChangeType = initialFKChangeType;

            DBChangesImporter importer = TestUtilsDBChangesImporter.CreateDBChangesImporter();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dbTable.ForeignKeys.Count);
            Assert.IsFalse(dbTable.PendingChangeApproved);
            Assert.AreEqual(tableChangeType, dbTable.PendingChangeType);
            Assert.AreEqual(initialFKChangeType, dbForeignKey.PendingChangeType);
            Assert.IsTrue(dbForeignKey.PendingChangeApproved);
            //---------------Execute Test ----------------------
            importer.ImportDBChanges(database, solution);
            //---------------Test Result -----------------------
            if (fkImported) dbForeignKey.AssertIsImported();
            else dbForeignKey.AssertIsNotImported();
        }
    }
}